package com.wyt.common.reflection;


import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;

/**
 * @TODO 插件开发工具类
 * 插件开发流程p:
 *  1、定义sdk接口项目
 *  2、定义sdk实现项目（插件）（注意再resources/META-INF.services目录下创建文件，文件名是sdk接口的全路径名，文件内容是接口实现类的全路径名）
 *  3、在平台中引入sdk接口项目的依赖
 *  4、将第二步打成的jar包放置指定目录，然后通过当前工具类的addUrlBySystemClassLoader方法即可将插件加载到当前工程中，并可以通过当前工具类的getSdkBean方法获取使用
 */
public class SdkPluginUtils {

    private static Map<Class, Object> map = new HashMap<>();

    private static Map<Class, Map<String, Object>> classMapMap = new HashMap<>();

    /**
     * 获取sdk接口的实现对象
     */
    public static <T> T getSdkBean(Class<T> c) {
        if (!map.containsKey(c)) {
            ServiceLoader<T> sdkServices = ServiceLoader.load(c);
            for (T sdkService : sdkServices) map.put(c, sdkService); //多个相同的sdk实现包，会覆盖存储
        }
        return (T) map.get(c);
    }

    /**
     * 获取系统类加载器，并通过反射调用系统类加载器的addURL方法将jar文件添加到类路径中。
     * @param path jar包所在目录
     * @param fileName jar包名称，如何该参数为空，则默认加载path路径下所有jar包
     */
    public static void addUrlBySystemClassLoader(String path, String fileName) throws Exception {
        URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();

        Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
        addURL.setAccessible(true);
        if (StringUtils.isBlank(fileName)) {
            File directory = new File(path);
            File[] files = directory.listFiles(((dir, name) -> name.endsWith(".jar")));
            if (files != null) {
                for (File file : files) addURL.invoke(classLoader, file.toURI().toURL());
            }
        } else {
            if (!path.endsWith("/")) path = path + File.separator;
            File file = new File(path + fileName);
            addURL.invoke(classLoader, file.toURI().toURL());
        }
    }


    /**
     * @TODO 卸载升级还处于雏形阶段，后期有时间再进行完善。前期先用着
     * 2、创建classloader、使用自定义classloader加载jar文件 (插件的卸载、升级 参考该方法)（插件的功能尽量单一，一个插件对应一个类加载器）
     * 卸载：销毁map中记录的创建bean、关闭对应的类加载器
     * 升级：销毁map中记录的创建bean、关闭对应的类加载器、重新创建一个类加载器
     * 获取加载的类
     * 卸载jar
     */
    public static void instanceClass() throws Exception {
        URLClassLoader classLoader = new URLClassLoader(new URL[]{new File("").toURI().toURL()});

        Class<?> loadedClass = classLoader.loadClass("com.example.MyClass");
        Object instance = loadedClass.getDeclaredConstructor().newInstance();
        Method method = loadedClass.getDeclaredMethod("methodName");
        method.invoke(instance);

        classLoader.close();
    }


}
